﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.ParallelArrays;
using FPA = Microsoft.ParallelArrays.FloatParallelArray;
using BPA = Microsoft.ParallelArrays.BoolParallelArray;
using IPA = Microsoft.ParallelArrays.IntParallelArray;
using PA = Microsoft.ParallelArrays.ParallelArrays;

namespace CoreCalc.SheetDefinedFunctions.Accellerator
{
    public class BinaryAccNode : AccNode
    {
        private delegate FPA binOp<A,B>(A a, B b);
        public AccNode Child1;
        public AccNode Child2;

        public enum Type
        {
            Add, Sub, Mul, Div, Max, Min, Pow, MMult
        };

        private Type type;
        
        public BinaryAccNode(Type t)
        {
            type = t;
        }


        static FPA test(AccNode a1, AccNode a2, binOp<FPA,FPA> b0, binOp<float,FPA> b1, binOp<FPA,float> b2, AccInputInfo info, int CallID)
        {
           
            if (a1 is ConstAccNode)
                return b1((a1 as ConstAccNode).Value, a2.GenerateFPAWithCache(info, CallID));
            else if (a2 is ConstAccNode)
                return b2(a1.GenerateFPAWithCache(info, CallID), (a2 as ConstAccNode).Value);
            else
                return b0(a1.GenerateFPAWithCache(info, CallID), a2.GenerateFPAWithCache(info, CallID));
        }

        


        public override FPA GenerateFPA(AccInputInfo info, int CallID)
        {
           

            switch(this.type)
            {
                case Type.Add:
                      return test(Child1, Child2, (binOp<FPA,FPA>)PA.Add, (binOp<float,FPA>)PA.Add, (binOp<FPA,float>)PA.Add,info,CallID);
                case Type.Sub:
                      return test(Child1, Child2, (binOp<FPA, FPA>)PA.Subtract, (binOp<float, FPA>)PA.Subtract, (binOp<FPA, float>)PA.Subtract,info,CallID);

                case Type.Mul:
                      return test(Child1, Child2, (binOp<FPA, FPA>)PA.Multiply, (binOp<float, FPA>)PA.Multiply, (binOp<FPA, float>)PA.Multiply,info,CallID);

                case Type.Div:
                      return test(Child1, Child2, (binOp<FPA, FPA>)PA.Divide, (binOp<float, FPA>)PA.Divide, (binOp<FPA, float>)PA.Divide,info,CallID);
                
                case Type.Max:
                      return test(Child1, Child2, (binOp<FPA, FPA>)PA.Max, (binOp<float, FPA>)PA.Max, (binOp<FPA, float>)PA.Max,info,CallID);

                case Type.Pow:
                    if (Child1 is ConstAccNode && (Child1 as ConstAccNode).Value == 2)
                        return PA.Pow2(Child1.GenerateFPAWithCache(info, CallID));
                    else
                        return PA.Pow(Child1.GenerateFPAWithCache(info, CallID), Child2.GenerateFPAWithCache(info, CallID));

                case Type.Min:
                    return test(Child1, Child2, (binOp<FPA, FPA>)PA.Min, (binOp<float, FPA>)PA.Min, (binOp<FPA, float>)PA.Add,info,CallID);

                case Type.MMult:
                    return PA.InnerProduct(Child1.GenerateFPAWithCache(info, CallID), Child2.GenerateFPAWithCache(info, CallID));
            }
            throw new Exception("Unknown Type in BinaryAccNode");
        }
    }
}
